using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
using Castle.Components.Validator;
using Castle.Services.Transaction;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Rhino.Commons;
using WineCellar.Business.Entities;
using WineCellar.Business.QueryObject;
using WineCellar.Business.ServiceInterfaces;
using WineCellar.Core.Interfaces;
using WineCellar.Web.Helpers;
using WineCellar.Web.Models;

namespace WineCellar.Web.Controllers
{
    [Transactional]
    public class WineController<T> : WineCellarController where T:Wine,new()
    {
        protected IEntityService<T> _wineService;
        protected IEntityService<Region> _regionService;
        protected IUserService _userService;

        public WineController(IEntityService<T> wineService,IUserService userService,IEntityService<Region> regionService)
        {
            _wineService = wineService;
            _userService = userService;
            _regionService = regionService;
        }

        public virtual ActionResult New()
        {
            return View("WineEdit", BuildEditViewData(new T()));
        }

        [Transaction(TransactionMode.Requires)]
        public virtual ActionResult Create()
        {
            var wine = new T();

            try
            {
                SmartBinder.UpdateFrom(wine, Request.Form);
                wine.AddedBy = _userService.GetUser(HttpContext.User.Identity.Name);
                _wineService.Save(wine);
            }
            catch (ValidationException e)
            {
                return new ErrorResult(e.ValidationErrorMessages);
            }
            return new SuccessResult();
        }

        protected WineEditViewData<T> BuildEditViewData(T wine)
        {
            var viewData = new WineEditViewData<T>();
            viewData.Wine = wine;
            var regions = new Region[0];
            viewData.Regions = wine.Region != null
                                   ? new SelectList(_regionService.GetAll(),"Id","Name",wine.Region ?? new Region())
                                   :
                                       new SelectList(_regionService.GetAll() ?? regions,"Id","Name");
            return viewData;
        }


        public ActionResult List()
        {
            return View();
        }

        [Transaction]
        public virtual ActionResult GenerateWines()
        {
            var user = _userService.GetUser(User.Identity.Name);
            var region = new Region() {AddedBy = user, DateAdded = DateTime.Now, Name = "Bordeaux"};
            Repository<Region>.Save(region);
            for (var i = 0; i <= 5;i++ )
            {
                var wine = new T()
                               {
                                   AddedBy = user,
                                   Appelation = "Medoc" + i,
                                   ColorType = "Red",
                                   DateAdded = DateTime.Now,
                                   Name = "Chateau Migraine" + i,
                                   Producer = "Lafitte" + i,
                                   Region = region
                               };
                _wineService.Save(wine);
            }
            return View();
        }

        [Transaction]
        public virtual ActionResult GetWines(int start, int limit, string sort, string dir, string query)
        {
            var searchObject = new WineSearchObject();
            FutureQueryOf<T> wines;
            ICollection<T> luceneWines;
            JObject json;

            if (!String.IsNullOrEmpty(query))
            {
                var serializer = new JsonSerializer();
                var jsArray =
                    (JArray)
                    serializer.Deserialize(new JsonTextReader(new StringReader(this.ReadFromRequest("fields"))));
                luceneWines = _wineService.LuceneSearch(query, jsArray.ToArray());
                json = GetJson(luceneWines, luceneWines.Count);
            }
            else
            {
                var formValues = PutRequestFilterValuesInNameValueCollection();
                if (formValues.Count > 0)
                {
                    SmartBinder.UpdateFrom(searchObject, formValues);
                    wines = _wineService.Search(searchObject, start, limit, String.IsNullOrEmpty(sort) ? "Name" : sort,
                                                dir);
                }
                else
                {
                    wines = _wineService.GetAll(start, limit, String.IsNullOrEmpty(sort) ? "Name" : sort, dir);
                }
                json = GetJson(wines.Results, wines.TotalCount);
            }
            return new JObjectResult(json);
        }

        protected virtual JObject GetJson(IEnumerable<T> wines, int totalCount)
        {
            return new JObject(
                new JProperty("totalcount", totalCount),
                new JProperty("root",
                              new JArray(
                                  from c in wines
                                  select new JObject(
                                      new JProperty("Id", c.Id.ToString()),
                                      new JProperty("Name", c.Name),
                                      new JProperty("Appelation", c.Appelation),
                                      new JProperty("Producer", c.Producer)))));
        }
    }

    //non generic implementation to make core work
    public class WineController : WineController<Wine>
    {
        public WineController(IEntityService<Wine> wineService, IUserService userService, IEntityService<Region> regionService)
            : base(wineService, userService, regionService)
        {
        }
    }
}